#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <list>

#define EVENT_NUM 64

void setnobloking(int fd)
{
    int old_status = fcntl(fd,F_GETFL,0);
    fcntl(fd,F_SETFL,old_status | O_NONBLOCK);
}

int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        printf("Usage:%s <port>\n",argv[0]);
        return 0;
    }
    int port = atoi(argv[1]);

    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    assert(listenfd != -1);

    int val = 1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));

    sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = INADDR_ANY;
    int n = bind(listenfd,(const sockaddr *)&server,sizeof(server));
    assert(n != -1);

    n = listen(listenfd,5);
    assert(n != -1);

    int epfd = epoll_create(5);// 创建epoll例程
    epoll_event *pee = new epoll_event[EVENT_NUM];// 保存触发事件信息
    
    // 注册监听套接字事件
    setnobloking(listenfd);
    epoll_event event;
    event.data.fd = listenfd;
    event.events = EPOLLIN;
    n = epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&event);
    assert(n != -1);

    std::list<int> client_list;// 管理客户端套接字

    while(true)
    {
        int ret = epoll_wait(epfd,pee,EVENT_NUM,-1);
        if(ret == -1)
        {
            printf("epoll_wait error!\n");
            break;
        }
        
        printf("有事件触发!个数: %d\n",ret);
        for(int i=0;i<ret;i++)
        {
            if(pee[i].data.fd == listenfd)// 如果是监听套接字事件触发
            {
                sockaddr_in client;
                socklen_t len = sizeof(client);
                int connfd = accept(pee[i].data.fd,(sockaddr *)&client,&len);
                client_list.push_back(connfd);
                setnobloking(connfd);
                // 注册客户端套接字事件
                epoll_event event;
                event.data.fd = connfd;
                event.events = EPOLLIN | EPOLLOUT | EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&event);
                printf("接收到新的客户端连接: %d\n",connfd);
            }
            else 
            {
                char read_buffer[2048];
                int recv_len = 0;
                while(true)
                {
                    recv_len = recv(pee[i].data.fd,read_buffer,sizeof(read_buffer)-1,0);
                    if(recv_len > 0)
                    {
                        read_buffer[recv_len] = 0;
                        for(auto &e:client_list)
                        {
                            if(e == pee[i].data.fd) continue;
                            printf("已向%d号客户端发送消息!\n",e);
                            send(e,read_buffer,strlen(read_buffer),0);
                        }
                    }
                    else if(recv_len == 0)// 对方连接关闭
                    {
                        epoll_ctl(epfd,EPOLL_CTL_DEL,pee[i].data.fd,nullptr);
                        close(pee[i].data.fd);
                        client_list.remove(pee[i].data.fd);
                        printf("客户端退出!%d\n",pee[i].data.fd);
                        break;// 不需要再读取数据了
                    }
                    else 
                    {
                        if(errno == EAGAIN) break;// 没有数据
                    }
                }
            }
        }
    }
    return 0;
}